package com.ccteam.fluidmusic.fluidmusic.media.library

import android.support.v4.media.MediaMetadataCompat
import com.ccteam.fluidmusic.fluidmusic.common.utils.MediaIDHelper
import com.ccteam.fluidmusic.fluidmusic.media.extensions.id
import com.ccteam.fluidmusic.fluidmusic.media.scanner.MediaStoreScanner
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton

/**
 * @author Xiaoc
 * @since 2020/12/26
 *
 * 音乐资源存储树，用来存储并调用远程或本地音乐的信息
 * 提供一些API进行对应内容的获取返回
 *
 * 该类为单例，全局调用一个资源库，归由Hilt管理
 *
 * */
@Singleton
class BrowseTree @Inject constructor(
    private val mediaIDHelper: MediaIDHelper,
) {

    @Inject
    lateinit var localMusicScanner: MediaStoreScanner

    /**
     * 本地音乐存储树，存储着获取到的相应音乐信息列表
     */
    private val localMediaItemTree = mutableMapOf<String, List<MediaMetadataCompat>>()

    private val onlineMediaItemTree = mutableMapOf<String,MutableList<MediaMetadataCompat>>()

    init {
        localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ROOT] =
            mediaIDHelper.generateLocalRootChildren()
    }

    /**
     * 根据parentId得到对应内容
     * 会根据parentId进行判断是本地还是在线，返回对应的MediaList内容
     * 没有该项会返回 null
     */
    fun getMediaList(parentId: String): List<MediaMetadataCompat>? {
        return if(mediaIDHelper.checkLocalOrOnlineId(parentId)){
            localMediaItemTree[parentId]
        } else {
            onlineMediaItemTree[parentId]
        }
    }

    /**
     * 添加在线音乐暂存数据
     * 将在线音乐数据存储在这里，原理和本地音乐类似
     *
     * @param parentId 存储在线音乐信息的Key
     * @param items 音乐信息列表
     */
    fun addOnlineMediaItems(parentId: String,items: List<MediaMetadataCompat>){
        if(items.isEmpty()){
            return
        }
        onlineMediaItemTree[parentId] = items.toMutableList()
    }

    /**
     * 添加在线音乐暂存数据
     * 将在线音乐数据存储在这里，原理和本地音乐类似
     * 该方法适合分页时不是整个列表全部赋值进入时使用
     * 此时需要单个赋值，该方法会检查如果存在该音乐ID则不进行加入，否则加入到列表中
     *
     * @param parentId 存储在线音乐信息的Key
     * @param item 音乐信息
     */
    fun addOnlineMediaItem(parentId: String,item: MediaMetadataCompat){
        if(onlineMediaItemTree[parentId] == null){
            onlineMediaItemTree[parentId] = mutableListOf()
        }
        val result = onlineMediaItemTree[parentId]!!.find {
            it.id == item.id
        }
        // 如果在其中没找到该歌曲，则将该歌曲加入到其中
        if(result == null){
            onlineMediaItemTree[parentId]!!.add(item)
        }
    }

    /**
     * 根据传来的MediaId来进行本地音乐对应内容的检索
     * 先要进行初始化判断，如果未初始化需要先进行最基本的初始化操作
     * @param parentId 父节点ID
     * @param callback 加载完成的回调
     * @param isRescan 是否重新扫描媒体库
     *
     * 该方法必须使用协程调用，因为存在阻塞方法
     */
    suspend fun getChildren(parentId: String, callback: LoadCallback, isRescan: Boolean) {
        withContext(Dispatchers.IO){
            if (isRescan) {
                localMediaItemTree.clear()
                localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ROOT] =
                    mediaIDHelper.generateLocalRootChildren()
            }
            getLocalChildrenByParentId(parentId, callback)
        }
    }

    /**
     * 得到本地音乐对应分类的数据
     * 采用懒加载方式，即要获取对应分类时，才去加载对应内容
     *
     * 该方法必须同步，否则可能会出现不同步的问题
     */
    @Synchronized
    private fun getLocalChildrenByParentId(parentId: String, callback: LoadCallback) {
        // 如果当前项存在，则直接返回对应内容
        localMediaItemTree[parentId]?.let {
            callback.onSuccess(localMediaItemTree[parentId])
            return
        }
        when (mediaIDHelper.extractBrowseRootId(parentId)) {
            MediaIDHelper.LOCAL_MEDIA_ALL -> {
                localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ALL] =
                    localMusicScanner.scanAllFromMediaStore().toMutableList()
                callback.onSuccess(localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ALL])
            }
            MediaIDHelper.LOCAL_MEDIA_ALBUM -> {
                // 扫描所有专辑内容
                if(localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ALBUM].isNullOrEmpty()){
                    localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ALBUM] =
                        localMusicScanner.scanAlbumFromMediaStore().toMutableList()
                }
                if (parentId == MediaIDHelper.LOCAL_MEDIA_ALBUM) {
                    callback.onSuccess(localMediaItemTree[parentId])
                } else {
                    // 如果浏览的是专辑子项，则继续搜索该专辑子项的内容
                    localMediaItemTree[parentId] = handleAlbumInnerMusic(parentId).toMutableList()
                    callback.onSuccess(localMediaItemTree[parentId])
                }
            }
            MediaIDHelper.LOCAL_MEDIA_ARTIST ->{
                // 扫描所有歌手内容
                if(localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ARTIST].isNullOrEmpty()){
                    localMediaItemTree[MediaIDHelper.LOCAL_MEDIA_ARTIST] =
                        localMusicScanner.scanArtistFromMediaStore().toMutableList()
                }
                when {
                    parentId == MediaIDHelper.LOCAL_MEDIA_ARTIST -> {
                        callback.onSuccess(localMediaItemTree[parentId])
                    }
                    mediaIDHelper.extractMusicOrBrowseIdFromMediaId(parentId)
                            == MediaIDHelper.LOCAL_MEDIA_MUSIC -> {
                        // 如果是显示该歌手所有的歌曲
                        localMediaItemTree[parentId] = handleArtistInnerMusic(
                            mediaIDHelper.extractBehindFirstMediaId(parentId)).toMutableList()
                        callback.onSuccess(localMediaItemTree[parentId])
                    }
                    mediaIDHelper.extractMusicOrBrowseIdFromMediaId(parentId)
                            == MediaIDHelper.LOCAL_MEDIA_ALBUM -> {
                        // 如果是显示该歌手所有的专辑
                        localMediaItemTree[parentId] = handleArtistInnerAlbum(
                            mediaIDHelper.extractBehindFirstMediaId(parentId)).toMutableList()
                        callback.onSuccess(localMediaItemTree[parentId])
                    }
                    else ->{
                        localMediaItemTree[parentId] = mediaIDHelper.generateLocalArtistChildren(
                            mediaIDHelper.extractMusicOrBrowseIdFromMediaId(parentId)
                        )
                        callback.onSuccess(localMediaItemTree[parentId])
                    }
                }
            }
            else ->{
                callback.onFail("不支持的分类：$parentId")
            }
        }
    }

    /**
     * 处理专辑内部子项信息
     * 该方法会调用 [MediaStoreScanner] 进行对应专辑子项的搜索
     */
    private fun handleAlbumInnerMusic(parentId: String): List<MediaMetadataCompat> {
        return localMusicScanner.scanAlbumMusic(
            mediaIDHelper.extractMusicOrBrowseIdFromMediaId(
                parentId
            )
        )
    }

    private fun handleArtistInnerMusic(noRootParentId: String): List<MediaMetadataCompat> {
        return localMusicScanner.scanArtistMusic(
            mediaIDHelper.extractBrowseRootId(
                noRootParentId
            )
        )
    }

    private fun handleArtistInnerAlbum(noRootParentId: String): List<MediaMetadataCompat> {
        return localMusicScanner.scanArtistAlbum(
            mediaIDHelper.extractBrowseRootId(
                noRootParentId
            )
        )
    }

    interface LoadCallback {
        fun onSuccess(metadataList: List<MediaMetadataCompat>?)
        fun onFail(errorMsg: String)
    }

}